package wechat.httpRequest;


import Utils.BizExceptionUtils;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import wechat.WxPayUtil;
import wechat.XMLUtil;
import wechat.entities.PayNotifyResult;
import wechat.entities.PayOrderParam;
import wechat.entities.UnfiedOrderParam;
import wechat.entities.UnfiedOrderResult;
import wechat.enums.ReturnCodeEnum;
import wechat.enums.WechatPayTradeTypeEnum;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;


/****
 * 微信支付
 */
public class WechatPayHttpRequest {


    private static final String unifiedorderUri = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    private static RestTemplate restTemplate = new RestTemplate();
    private static Logger log = LoggerFactory.getLogger(WechatPayHttpRequest.class);

    static {
        List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
        converterList.remove(1);    //移除StringHttpMessageConverter
        HttpMessageConverter<?> converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        converterList.add(1, converter);    //convert顺序错误会导致失败
        restTemplate.setMessageConverters(converterList);
    }



    private static UnfiedOrderResult PayUnifiedOrderResult(PayOrderParam param, String apiKey) {
        BizExceptionUtils.isEmpty(apiKey, "apiKey不能为空", true);
        UnfiedOrderParam unfiedOrderParam = checkUnifiedOrder(param);
        UnfiedOrderResult unfiedOrderResult = null;
        try {
            unfiedOrderResult = unifiedorder(unfiedOrderParam, apiKey);
        } catch (Exception e) {
            e.printStackTrace();
            BizExceptionUtils.bizException("微信支付接口调用异常");
        }
        //返回状态码  处理
        if (ReturnCodeEnum.失败.getCode().equals(unfiedOrderResult.getReturn_code())) {
            BizExceptionUtils.bizException(unfiedOrderResult.getReturn_msg());
        } else if (ReturnCodeEnum.成功.getCode().equals(unfiedOrderResult.getReturn_code()) && ReturnCodeEnum.失败.getCode().equals(unfiedOrderResult.getResult_code())) {
            BizExceptionUtils.bizException(unfiedOrderResult.getErr_code(), unfiedOrderResult.getErr_code_des());
        } else if (ReturnCodeEnum.成功.getCode().equals(unfiedOrderResult.getReturn_code()) && ReturnCodeEnum.成功.getCode().equals(unfiedOrderResult.getResult_code())) {
            return unfiedOrderResult;
        }
        return unfiedOrderResult;

    }


    /***
     * 微信统一支付调用
     * 返回 微信支付预支付结果
     *      UnfiedOrderResult 不为空 为调用微信预支付调用成功
     * @param param 支付参数
     * @param apiKey apiKey 由微信支付商户提供
     * @return
     */
    public static UnfiedOrderResult weChantPayOrder(PayOrderParam param, String apiKey) {
        UnfiedOrderResult unfiedOrderResult = PayUnifiedOrderResult(param, apiKey);
        return unfiedOrderResult;
    }

    /***
     *@务必注意:
     *      必须(交易类型位JSAPI)在调用weChantPayOrder(PayOrderParam param, String apiKey)
     *      前提下下使用且发起微信预支付成功返回之后,封装返回给前端同学的参数
     * @param result  预支付结果(这里必须确认 调用微信预支付接口是成功的)
     * @param apiKey  apiKey由微信支付商户提供
     * @return Map 返给前端的参数
     * 封装参数:{
     *                "appId":"wx2421b1c4370ec43b",     //公众号名称，由商户传入
     *                "timeStamp":"1395712654",         //时间戳，自1970年以来的秒数
     *                "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
     *                "package":"prepay_id=u802345jgfjsdfgsdg888",
     *                "signType":"MD5",         //微信签名方式：
     *                "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
     *          }
     */
    public static Map<String, Object> WechantPayReturnByJSAPI(UnfiedOrderResult result, String apiKey) {
        Long timestamp = System.currentTimeMillis() / 1000;
        Map<String, Object> data = new HashMap<>();
        data.put("timeStamp", timestamp + "");
        data.put("nonceStr", timestamp + "");
        data.put("package", "prepay_id=" + result.getPrepay_id());
        data.put("signType", "MD5");
        data.put("appId", result.getAppid());
        SortedMap<Object, Object> params = new TreeMap<>();
        params.put("appId", result.getAppid());
        params.put("signType", "MD5");
        params.put("package", "prepay_id=" + result.getPrepay_id());
        params.put("nonceStr", timestamp + "");
        params.put("timeStamp", timestamp + "");
        log.debug("生成sign 参数" + params.toString());
        String sign = WxPayUtil.createSign("UTF-8", params, apiKey);
        log.debug("返回的appid:" + result.getAppid());
        log.debug("返回的package:" + "prepay_id=" + result.getPrepay_id());
        log.debug("返回的timeStamp:" + "timeStamp=" + timestamp);
        log.debug("生成sign:" + sign);
        data.put("paySign", sign);
        return data;
    }


    /***
     * 回调函数校验
     *   若校验成功则 返回参数
     *   若校验失败 返回空
     * @param request
     * @param apiKey apiKey
     */
    public static PayNotifyResult checkPayCallBack(HttpServletRequest request, String apiKey) throws Exception {
        InputStream inputStream;
        StringBuffer sb = new StringBuffer();
        inputStream = request.getInputStream();
        String s;
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
        while ((s = in.readLine()) != null) {
            sb.append(s);
        }
        in.close();
        inputStream.close();
        //读取到的是xml,需要解析成map
        log.info("微信支付回调请求返回---");
        log.info(sb.toString());
        log.info("----");
        Map<String, String> resultMap = XMLUtil.doXMLParse(sb.toString());
        SortedMap<Object, Object> packageparams = new TreeMap<>();
        for (Object key : resultMap.keySet()) {
            String value = resultMap.get(key);
            String v = "";
            if (null != value) {
                v = value.trim();
            }
            packageparams.put(key, v);
        }
        //todo
        //验证签名是否正确
        if (WxPayUtil.isTenpaySign("UTF-8", packageparams, apiKey)) {
            PayNotifyResult payNotifyResult = JSONObject.parseObject(JSONObject.toJSONString(resultMap), PayNotifyResult.class);
            if (payNotifyResult == null) {
                return null;
            }
            if (ReturnCodeEnum.失败.getCode().equals(payNotifyResult.getResult_code())) {
                return null;
            }
            return payNotifyResult;

        } else {
            return null;
        }
    }

    /****
     * 响应微信支付通知
     * @param response response
     * @param isSuccess  回调函数校验 true 成功 false 失败
     * @throws IOException
     */
    public static void payCallBackResponse(HttpServletResponse response, boolean isSuccess, String tip) throws IOException {
        String resXml;
        if (true == isSuccess) {
            resXml = "<xml>"
                    + "<return_code><![CDATA[SUCCESS]]></return_code>"
                    + "<return_msg><![CDATA[OK]]></return_msg>"
                    + "</xml>";
        } else {
            resXml = "<xml>"
                    + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[" + tip + "]]></return_msg>"
                    + "</xml>";
        }
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();

    }

    /***
     * 微信统一支付接口
     * 请使用本类中方法
     *   @See weChantPayOrder(UnfiedOrderParam unfiedOrderParam, String apiKey)
     * @param unfiedOrderParam
     */
    private static UnfiedOrderResult unifiedorder(UnfiedOrderParam unfiedOrderParam, String key) throws Exception {
        SortedMap<Object, Object> param = new TreeMap<>();

        String nonceStr = System.currentTimeMillis() + "";
        //appid
        param.put("appid", unfiedOrderParam.getAppid());
        //商户码
        param.put("mch_id", unfiedOrderParam.getMch_id());
        //随机字符串
        param.put("nonce_str", nonceStr);
        //商品描述 格式:  腾讯公司-充值
        param.put("body", new String(unfiedOrderParam.getBody()));
        //订单号 需在应用系统中唯一
        param.put("out_trade_no", unfiedOrderParam.getOut_trade_no());
        //付款方式
        param.put("fee_type", unfiedOrderParam.getFee_type());
        //钱数  以分为单位
        param.put("total_fee", unfiedOrderParam.getTotal_fee());
        //终端 根据trade_type不同 ip取值不同
        param.put("spbill_create_ip", unfiedOrderParam.getSpbill_create_ip());
        //回调地址
        param.put("notify_url", unfiedOrderParam.getNotify_url());
        //交易类型
        param.put("trade_type", unfiedOrderParam.getTrade_type());
        if (!WechatPayTradeTypeEnum.扫码支付.getCode().equals(unfiedOrderParam.getTrade_type())) {
            //微信用户 openid
            param.put("openid", unfiedOrderParam.getOpenid());
        }
        //签名类型
        param.put("sign_type", "MD5");
        String sign = WxPayUtil.createSign("UTF-8", param, key);
        param.put("sign", sign);

        //封装参数为 xml;
        String xmlRequest = WxPayUtil.getRequestXml(param);
        System.out.println("微信支付请求参数---");
        System.out.println(xmlRequest);
        System.out.println("---");
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("text/xml; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());


        String result = restTemplate.postForObject(unifiedorderUri, xmlRequest, String.class);
        log.info("微信支付请求返回---");
        log.info(result);
        log.info("----");
        Map resultMap = XMLUtil.doXMLParse(result);
        UnfiedOrderResult unfiedOrderResult = JSONObject.parseObject(JSONObject.toJSONString(resultMap), UnfiedOrderResult.class);
        return unfiedOrderResult;

    }

    /***
     * 微信支付接口
     * @param param
     */
    private static UnfiedOrderParam checkUnifiedOrder(PayOrderParam param) {
        String appId = param.getAppId();
        String mchId = param.getMchId();
        String body = param.getBody();
        String outTradeNo = param.getOutTradeNo();
        String feeType = param.getFeeType();
        Integer totalFee = param.getTotalFee();
        String tradeType = param.getTradeType();
        String spBillCreateIp = param.getSpBillCreateIp();
        String notNotifyUrl = param.getNotifyUrl();
        String openId = param.getOpenId();
        BizExceptionUtils.isEmpty(appId, "公众账号ID不能为空", true);
        BizExceptionUtils.isEmpty(mchId, "商户号不能为空", true);
        BizExceptionUtils.isEmpty(body, "商品描述[body字段]不能为空", true);
        BizExceptionUtils.isEmpty(outTradeNo, "交易编号不能为空", true);
        BizExceptionUtils.isEmpty(feeType, "标价币种不能为空", true);
        BizExceptionUtils.isEmpty(totalFee, "标价金额不能为空", false);
        BizExceptionUtils.isTrue(totalFee < 1, "标价金额不能小于1");
        BizExceptionUtils.isEmpty(notNotifyUrl, "回调函数地址不能为空", true);
        BizExceptionUtils.isEmpty(tradeType, "交易类型不能为空", true);
        UnfiedOrderParam unfiedOrderParam = new UnfiedOrderParam();
        unfiedOrderParam.setAppid(appId);
        unfiedOrderParam.setMch_id(mchId);
        unfiedOrderParam.setBody(body);
        unfiedOrderParam.setOut_trade_no(outTradeNo);
        unfiedOrderParam.setFee_type(feeType);
        unfiedOrderParam.setTotal_fee(totalFee);
        unfiedOrderParam.setSpbill_create_ip(spBillCreateIp);
        unfiedOrderParam.setNotify_url(notNotifyUrl);
        unfiedOrderParam.setOpenid(openId);
        unfiedOrderParam.setTrade_type(tradeType);
        if (!WechatPayTradeTypeEnum.扫码支付.getCode().equals(unfiedOrderParam.getTrade_type())) {
            BizExceptionUtils.isEmpty(unfiedOrderParam.getOpenid(), "非扫码支付openId不能为空", false);
        }
        return unfiedOrderParam;
    }
}


